﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using CompactMapper.Orm.Annotations;
using CompactMapper.Orm.Extensions;
using CompactMapper.Orm.UnitOfWork.Mappings.Meta.PropertyInfos.Columns;
using CompactMapper.Orm.UnitOfWork.Mappings.Meta.PropertyInfos.Relations;
using CompactMapper.Orm.UnitOfWork.Mappings.Meta.PropertyInfos.Indexes;

namespace CompactMapper.Orm.UnitOfWork.Mappings.Meta.PropertyInfos
{
    public class PropertyInfoMeta
    {
        protected readonly RuntimeMethodHandle GetMethodHandle;
        protected readonly RuntimeMethodHandle SetMethodHandle;

        public PropertyInfoMeta(PropertyInfo propertyInfo)
        {
            if (propertyInfo.CanRead)
                GetMethodHandle = propertyInfo.GetGetMethod().MethodHandle;
            if (propertyInfo.CanWrite)
                SetMethodHandle = propertyInfo.GetSetMethod().MethodHandle;
        }


        public object GetValue(object target)
        {
            var getter = MethodBase.GetMethodFromHandle(GetMethodHandle) as MethodInfo;
            var value = getter.Invoke(target, new object[] { });
            return value;
        }

        internal void SetValue(object target, object value)
        {
            var setter = MethodBase.GetMethodFromHandle(SetMethodHandle) as MethodInfo;
            setter.Invoke(target, new[] { value });
        }

        internal static IEnumerable<PropertyInfoMeta> GetPersistencePropertyInfoMetas(Type type)
        {
            var all = from p in type.GetProperties()
                      from persistence in p.GetCustomAttributes(typeof(PersistenceAttribute), false)
                      select new PropertyInfoPersistenceAttributePair(p, (PersistenceAttribute)persistence);
            var relationPropertyInfoMetas =
                all.Where(a => a.PersistenceAttribute is RelationshipAttribute).Select(pair => Relation.From(pair)).Cast
                    <PropertyInfoMeta>();
            var columnsPropertyInfoMetas =
                all.Where(a => a.PersistenceAttribute is ColumnAttribute).Select(pair => Column.From(pair)).Cast
                    <PropertyInfoMeta>();
            var indexesPropertyInfoMetas =
                all.Where(a => a.PersistenceAttribute is IndexAttribute).Select(pair => Index.From(pair)).Cast
                    <PropertyInfoMeta>();
            return relationPropertyInfoMetas.Concat(columnsPropertyInfoMetas).Concat(indexesPropertyInfoMetas);
        }
    }
}